﻿using System.Drawing;
using System.Linq;
using System.Collections.Generic;
using System;
using PaddleOCRSharp;

namespace FeiYang
{
    public class 坐标_
    {
        public 坐标_(int X, int Y)
        {
            this.X = X;
            this.Y = Y;
        }

        public static 坐标_ 零值()
        {
            return new 坐标_(-1, -1);
        }
        public int X { get; set; }
        public int Y { get; set; }
        public override string ToString()
        {
            return "X : " + this.X.ToString() + ", " + "Y : " + this.Y.ToString();
        }

        public override bool Equals(object obj)
        {
            return obj is 坐标_ v &&
                   X == v.X &&
                   Y == v.Y;
        }
        public static bool operator ==(坐标_ L, 坐标_ R)
        {
            return L.Equals(R);
        }
        public static bool operator !=(坐标_ L, 坐标_ R)
        {
            return !L.Equals(R);
        }

        public static explicit operator Point(坐标_ 坐标)
        {
            return new Point(坐标.X, 坐标.Y);
        }
    }

    public enum 点属性_
    {
        无_ = 0,
        门点_ = 1,
        极品怪点_ = 2,
        头目点_ = 3,
        王点_ = 4,
        账号安全员_ = 5,
        传送_ = 6,
        传送门_ = 7,
        仓库_ = 8,
        商人_ = 9,
    }
    public enum 怪物属性_
    {
        无_ = 0,
        远程_ = 1 << 0,
        极品_ = 1 << 1,
        头目_ = 1 << 2,
        Boss = 1 << 3
    }

    public enum 地图分类_
    {
        无_,
        小黑屋_,
        跃马大平原_,
        炼狱三职业_,
        炼狱道士_,
        炼狱法师_,
        炼狱战士_,
        炼狱魔境_,
        矿区_,
        将军坟_,
        将军东_,
        机关洞_,
        蛇魔谷_,
        逆魔古刹_,
        禁地_,
        通天塔_,
        玄冰古道东_,
        玄冰古道西_,
        玄冰古道南_,
        地下魔域_,
        铁血魔城_,
        修罗神殿_,
        魔隐村_,
        骑兵竞技场_,
        幽灵海_,
        沙城仓库_,
        声望地图_,
    }

    public enum 地图属性_
    {
        无_ = 0,
        未注册地图_ = 1 << 0,
        安全_ = 1 << 1,
        法师_ = 1 << 2,
        道士_ = 1 << 3,
        战士_ = 1 << 4,
        需要条件_ = 1 << 5,
        可以飞书_ = 1 << 6,
        下线不在本层_ = 1 << 7,
        不可随机_ = 1 << 8,
        不可瞬移_ = 1 << 9,
        不可内挂_ = 1 << 10,
        大型地图_ = 1 << 11,
        中型地图_ = 1 << 12,
        小型地图_ = 1 << 13,
        多图同名_ = 1 << 14,
        高级地图_ = 1 << 15,
    }

    public class 地图点_
    {
        public 地图点_(点及坐标_ 位置, 点属性_ 属性, 地图_ 所在地图)
        {
            this.位置 = 位置;
            this.属性 = 属性;
            this.所在地图 = 所在地图;
            this.支持多点 = new List<点及坐标_> { 位置 };
            this.到达附近就可以 = 属性 != 点属性_.无_ && 属性 != 点属性_.门点_;
        }

        public 地图点_(点属性_ 属性, 地图_ 所在地图) : this(new 点及坐标_(new Point(-1, -1), 坐标_.零值()), 属性, 所在地图)
        { }
        public 点及坐标_ 位置 { get; private set; }
        public 点属性_ 属性 { get; private set; }
        public 地图_ 所在地图 { get; set; }
        public List<点及坐标_> 支持多点 { get; set; }
        public virtual string 名称 { get; set; }
        public virtual string 识别名 { get; set; }
        public bool 到达附近就可以 { get; set; }
        public bool 实现快速接近 { get; set; }
        public virtual 任务名_ 快速接近(挂机人物_ 人物)
        {
            throw new Exception("未实现");
        }
    }

    public class Npc_ : 地图点_
    {
        public Npc_(点属性_ 属性, string 名称, string 识别名, 地图_ 所在地图)
            : this(new 点及坐标_(new Point(-1, -1), 坐标_.零值()), 属性, 名称, 识别名, 所在地图)
        { }
        public Npc_(点及坐标_ 位置, 点属性_ 属性, string 名称, string 识别名, 地图_ 所在地图)
            : base(位置, 属性, 所在地图)
        {
            this.名称 = 名称;
            this.识别名 = 识别名;
        }
        public Npc_(Point 像素点, 坐标_ 位置, 点属性_ 属性, string 名称, string 识别名, 地图_ 所在地图)
            : this(new 点及坐标_(像素点, 位置), 属性, 名称, 识别名, 所在地图) { }
        public override string 名称 { get; set; }
        public override string 识别名 { get; set; }
    }

    public class 快捷传送_ : Npc_
    {
        public 快捷传送_(string 名称, string 识别名, List<地图_> 目标地图) : this(new Point(-1, -1), 坐标_.零值(), 点属性_.传送_, 名称, 识别名, null, 目标地图)
        { }
        public 快捷传送_(string 名称, string 识别名, 地图_ 所在地图, List<地图_> 目标地图) : this(new Point(-1, -1), 坐标_.零值(), 点属性_.传送_, 名称, 识别名, 所在地图, 目标地图)
        { }
        public 快捷传送_(点属性_ 属性, string 名称, string 识别名, 地图_ 所在地图, List<地图_> 目标地图) : this(new Point(-1, -1), 坐标_.零值(), 属性, 名称, 识别名, 所在地图, 目标地图)
        { }

        public 快捷传送_(点及坐标_ 位置, 点属性_ 属性, string 名称, string 识别名, 地图_ 所在地图, List<地图_> 目标地图) : base(位置, 属性, 名称, 识别名, 所在地图)
        {
            this.目标地图 = 目标地图;
            if (this.所在地图 != null)
            {
                this.所在地图.添加门点(this);
            }
        }
        public 快捷传送_(Point 像素点, 坐标_ 位置, 点属性_ 属性, string 名称, string 识别名, 地图_ 所在地图, List<地图_> 目标地图) : this(new 点及坐标_(像素点, 位置), 属性, 名称, 识别名, 所在地图, 目标地图)
        { }

        public List<地图_> 目标地图 { get; set; }
        public virtual bool 传送(挂机人物_ _, 地图_ __)
        {
            throw new Exception("未实现传送");
        }
    }

    public class 回城_ : 快捷传送_
    {
        public 回城_() : base("回城", "使用回城", new List<地图_> { 地图集_.获取地图("跃马大平原") }) { }
        public override bool 传送(挂机人物_ 人物, 地图_ _)
        {
            人物.输出("使用回城石:");
            return UI_.使用回城();
        }
    }

    public class 去挖矿_ : 快捷传送_
    {
        public 去挖矿_() : base("去挖矿_", "传送", new List<地图_> { 地图集_.获取地图("矿区入口") }) { }

        public override bool 传送(挂机人物_ 人物, 地图_ __)
        {
            UI_.打开今日挑战();
            人物.输出("去挖矿任务");
            其他_.延迟(其他_.时长_400);
            键鼠_.左键单击(421, 447);
            var 到达 = UI_.判断地图名("矿区入口");
            其他_.延迟(其他_.时长_1400);
            if (!到达) throw new Exception("去矿区入口传送失败");
            return 到达;
        }
    }

    public class 去王城任务_ : 快捷传送_
    {
        public 去王城任务_() : base("去王城任务", "传送", new List<地图_> { 地图集_.获取地图("跃马大平原") }) { }

        public override bool 传送(挂机人物_ 人物, 地图_ __)
        {
            UI_.打开今日挑战();
            人物.输出("去王城任务");
            其他_.延迟(其他_.时长_400);
            键鼠_.左键单击(421, 309);
            var 到达 = UI_.判断地图名("跃马大平原");
            其他_.延迟(其他_.时长_1400);
            if (!到达) throw new Exception("去王城任务传送失败");
            return 到达;
        }
    }

    public class 去马倌_ : 快捷传送_
    {
        public 去马倌_() : base("去马倌_", "传送", new List<地图_> { 地图集_.获取地图("跃马大平原") }) { }
        public override bool 传送(挂机人物_ 人物, 地图_ __)
        {
            UI_.打开今日挑战();
            人物.输出("去马倌");
            其他_.延迟(其他_.时长_400);
            键鼠_.左键单击(417, 400);
            var 到达 = UI_.判断地图名("跃马大平原");
            if (!到达) throw new Exception("去马倌传送失败");
            到达 = UI_.坐标点距离较近(UI_.连续识别坐标(), new 坐标_(184, 811), 4);
            if (!到达) throw new Exception("去马倌传送失败");
            return 到达;
        }
    }

    public class 去声望老人_ : 快捷传送_
    {
        public 去声望老人_() : base("去声望老人", "传送", new List<地图_> { 地图集_.获取地图("跃马大平原") }) { }

        public override bool 传送(挂机人物_ 人物, 地图_ __)
        {
            UI_.打开今日挑战();
            人物.输出("去声望老人");
            其他_.延迟(其他_.时长_400);
            键鼠_.左键单击(421, 351);
            var 到达  = UI_.判断地图名("跃马大平原");
            其他_.延迟(其他_.时长_1400);
            if (!到达) throw new Exception("去声望老人失败");
            return 到达;
        }
    }

    public class 去拉镖活动_ : 快捷传送_
    {
        public 去拉镖活动_() : base("去拉镖活动", "传送", new List<地图_> { 地图集_.获取地图("跃马大平原") }) { }

        public override bool 传送(挂机人物_ 人物, 地图_ _)
        {
            UI_.打开今日挑战();
            人物.输出("去拉镖活动");
            其他_.延迟(其他_.时长_400);
            键鼠_.左键单击(421, 490);
            var 到达  = UI_.判断地图名("跃马大平原");
            其他_.延迟(其他_.时长_1400);
            if (!到达) throw new Exception("去土城镖师传送失败");
            return 到达;
        }
    }
    public class 去炼狱旋涡_ : 快捷传送_
    {
        public 去炼狱旋涡_() : base("去炼狱旋涡", "传送", new List<地图_> { 地图集_.获取地图("跃马大平原") }) { }
        public override bool 传送(挂机人物_ 人物, 地图_ __)
        {
            UI_.打开今日挑战();
            人物.输出("去炼狱旋涡");
            其他_.延迟(其他_.时长_400);
            键鼠_.左键单击(421, 624);
            var 到达  = UI_.判断地图名("跃马大平原");
            其他_.延迟(其他_.时长_1400);
            if (!到达) throw new Exception("去炼狱旋涡传送失败");
            return 到达;
        }
    }

    public class 传送_ : 快捷传送_
    {
        public 传送_(Point 像素点, 坐标_ 位置, 点属性_ 属性, string 名称, string 识别名, 地图_ 所在地图, List<地图_> 目标地图)
              : this(new 点及坐标_(像素点, 位置), 属性, 名称, 识别名, 所在地图, 目标地图) { }
        public 传送_(点及坐标_ 位置, 点属性_ 属性, string 名称, string 识别名, 地图_ 所在地图, List<地图_> 目标地图)
              : base(位置, 属性, 名称, 识别名, 所在地图, 目标地图) { }
        public 传送_(Point 像素点, 坐标_ 位置, string 名称, string 识别名, 地图_ 所在地图, List<地图_> 目标地图)
              : this(new 点及坐标_(像素点, 位置), 点属性_.传送_, 名称, 识别名, 所在地图, 目标地图) { }
        public 传送_(点及坐标_ 位置, string 名称, string 识别名, 地图_ 所在地图, List<地图_> 目标地图)
              : this(位置, 点属性_.传送_, 名称, 识别名, 所在地图, 目标地图) { }
        public 传送_(string 名称, string 识别名, 地图_ 所在地图, List<地图_> 目标地图)
              : this(new 点及坐标_(new Point(-1, -1), 坐标_.零值()), 名称, 识别名, 所在地图, 目标地图) { }
        public 传送_(点属性_ 属性, string 名称, string 识别名, 地图_ 所在地图, List<地图_> 目标地图)
              : this(new 点及坐标_(new Point(-1, -1), 坐标_.零值()), 属性, 名称, 识别名, 所在地图, 目标地图) { }
        public Rectangle 识别范围 { get; set; }
    }

    public class 门点_ : 传送_
    {
        public 门点_(点及坐标_ 位置, string 名称, string 识别名, 地图_ 所在地图, List<地图_> 目标地图)
       : base(位置, 点属性_.门点_, 名称, 识别名, 所在地图, 目标地图) { }
    }

    public class 传送门_ : 传送_
    {
        public 传送门_(点及坐标_ 位置, string 名称, string 识别名, 地图_ 所在地图, List<地图_> 目标地图)
            : base(位置, 点属性_.传送门_, 名称, 识别名, 所在地图, 目标地图) { }
        public 传送门_(Point 像素点, 坐标_ 位置, string 名称, string 识别名, 地图_ 所在地图, List<地图_> 目标地图)
            : base(new 点及坐标_(像素点, 位置), 点属性_.传送门_, 名称, 识别名, 所在地图, 目标地图) { }
    }

    public class 地下魔域一去地下魔域二_ : 门点_
    {
        public 地下魔域一去地下魔域二_() : base(new 点及坐标_(new Point(411, 298), new 坐标_(81, 31)), "地下魔域一去地下魔域二", "", 地图_.地下魔域一层, new List<地图_> { 地图_.地下魔域二层 }) { }

        public override bool 传送(挂机人物_ 人物, 地图_ _)
        {
            UI_.切换到走路();
            var p = UI_.连续识别坐标();
            var 结果 = 其他_.循环判断(() =>
            {
                UI_.移动(UI_.根据位置判断方向(p, new 坐标_(80, 33), out int t, false), t);
                p = UI_.连续识别坐标();
                return UI_.判断地图名("地下魔域二层", 1);
            }, 10, 200);
            if (!结果)
            {
                UI_.开启内挂();
                其他_.延迟(其他_.时长_1000);
            }
            UI_.切换到跑路();
            return 结果;
        }
    }

    public class 地下魔域二去地下魔域三_ : 门点_
    {
        public 地下魔域二去地下魔域三_() : base(new 点及坐标_(new Point(370, 365), new 坐标_(89, 91)), "地下魔域二去地下魔域三", "", 地图_.地下魔域二层, new List<地图_> { 地图_.地下魔域三层 })
        {
            this.支持多点.Add(new 点及坐标_(new Point(417, 531), new 坐标_(112, 252)));
            this.支持多点.Add(new 点及坐标_(new Point(699, 506), new 坐标_(247, 226)));
        }

        public override bool 传送(挂机人物_ 人物, 地图_ _)
        {
            UI_.切换到走路();
            var p = UI_.连续识别坐标();
            var 精确值 = new 坐标_[] { new 坐标_(91, 94), new 坐标_(114, 254), new 坐标_(246, 228) };
            var 索引 = 人物.随机门点序号;
            人物.随机门点序号 = -1;
            人物.输出("当前门点索引: " + 索引.ToString());
            人物.输出("当前门点坐标: " + 精确值[索引]);
            人物.输出("识别的坐标: " + p.ToString());
            var 结果 = 其他_.循环判断(() =>
            {
                UI_.移动(UI_.根据位置判断方向(p, 精确值[索引], out int t, false), t);
                p = UI_.连续识别坐标();
                return UI_.判断地图名("地下魔域三层", 1);
            }, 10, 200);
            if (!结果)
            {
                UI_.开启内挂();
                其他_.延迟(其他_.时长_1000);
            }
            UI_.切换到跑路();
            return 结果;
        }
    }

    public class 地下魔域三去地下魔域四_ : 门点_
    {
        public 地下魔域三去地下魔域四_() : base(new 点及坐标_(new Point(769, 327), new 坐标_(321, 78)), "地下魔域三去地下魔域四", "", 地图_.地下魔域三层, new List<地图_> { 地图_.地下魔域四层 })
        {
            this.支持多点.Add(new 点及坐标_(new Point(589, 420), new 坐标_(223, 181)));
        }

        public override bool 传送(挂机人物_ 人物, 地图_ _)
        {
            UI_.切换到走路();
            var p = UI_.连续识别坐标();
            var 精确值 = new 坐标_[] { new 坐标_(324, 74), new 坐标_(224, 180) };
            var 索引 = 人物.随机门点序号;
            人物.随机门点序号 = -1;
            人物.输出("当前门点索引: " + 索引.ToString());
            人物.输出("当前门点坐标: " + 精确值[索引]);
            人物.输出("识别的坐标: " + p.ToString());
            var 结果 = 其他_.循环判断(() =>
            {
                UI_.移动(UI_.根据位置判断方向(p, 精确值[索引], out int t, false), t);
                p = UI_.连续识别坐标();
                return UI_.判断地图名("地下魔域四层", 1);
            }, 10, 200);
            if (!结果)
            {
                UI_.开启内挂();
                其他_.延迟(其他_.时长_1000);
            }
            UI_.切换到跑路();
            return 结果;
        }
    }

    public class 地下魔域四去地下魔域五_ : 门点_
    {
        public 地下魔域四去地下魔域五_() : base(new 点及坐标_(new Point(411, 573), new 坐标_(80, 228)), "地下魔域四去地下魔域五", "", 地图_.地下魔域四层, new List<地图_> { 地图_.地下魔域五层 })
        {
        }

        public override bool 传送(挂机人物_ 人物, 地图_ _)
        {
            UI_.切换到走路();
            var p = UI_.连续识别坐标();
            var 结果 = 其他_.循环判断(() =>
            {
                UI_.移动(UI_.根据位置判断方向(p, new 坐标_(78, 231), out int t, false), t);
                p = UI_.连续识别坐标();
                return UI_.判断地图名("地下魔域五层", 1);
            }, 10, 200);
            if (!结果)
            {
                UI_.开启内挂();
                其他_.延迟(其他_.时长_1000);
            }
            UI_.切换到跑路();
            return 结果;
        }
    }

    public class 地下魔域五去地下魔域六_ : 门点_
    {
        public 地下魔域五去地下魔域六_() : base(new 点及坐标_(new Point(329, 581), new 坐标_(54, 251)), "地下魔域五去地下魔域六", "", 地图_.地下魔域五层, new List<地图_> { 地图_.地下魔域六层_1 })
        {
        }

        public override bool 传送(挂机人物_ 人物, 地图_ _)
        {
            UI_.切换到走路();
            var p = UI_.连续识别坐标();
            var 结果 = 其他_.循环判断(() =>
            {
                UI_.移动(UI_.根据位置判断方向(p, new 坐标_(57, 254), out int t, false), t);
                p = UI_.连续识别坐标();
                return UI_.判断地图名("地下魔域六层", 1);
            }, 10, 200);
            if (!结果)
            {
                UI_.开启内挂();
                其他_.延迟(其他_.时长_1000);
            }
            UI_.切换到跑路();
            return 结果;
        }
    }

    public class 炼狱光门_ : 传送门_
    {
        public 炼狱光门_(点及坐标_ 位置, string 名称, string 识别名, 地图_ 所在地图, List<地图_> 目标地图) : base(位置, 名称, 识别名, 所在地图, 目标地图)
        { }
    }
    public class 炼狱旋涡_ : 炼狱光门_
    {
        private Rectangle 炼狱时间 = Rectangle.FromLTRB(191, 148, 335, 173);
        private Rectangle 挑战炼狱 = Rectangle.FromLTRB(55, 190, 140, 210);
        private Rectangle 购买炼狱时间 = Rectangle.FromLTRB(185, 190, 300, 210);
        private Rectangle 购买按钮 = Rectangle.FromLTRB(362, 142, 417, 163);

        public 炼狱旋涡_() : base(new 点及坐标_(new Point(470, 592), new 坐标_(189, 830)), "炼狱旋涡", "炼狱之门", 地图_.跃马大平原, new List<地图_> { 地图_.海岛 })
        {
            this.实现快速接近 = true;
        }

        public override 任务名_ 快速接近(挂机人物_ 人物)
        {
            var 地图 = UI_.获取当前地图();
            if (地图 == 地图_.跃马大平原)
            {
                var p = UI_.连续识别坐标();
                var 较近 = UI_.坐标点距离较近(p, this.位置.坐标, 4);
                if (较近)
                {
                    UI_.关闭今日挑战();
                    return 任务名_.已经到达指定地点_;
                }
            }
            UI_.去炼狱旋涡.传送(人物, null);
            return 任务名_.已经到达指定地点_;
        }

        public override bool 传送(挂机人物_ 人物, 地图_ 目的地)
        {
            if (目的地 != 地图_.海岛) return false;
            var 找到 = UI_.鼠标点击屏幕坐标(187, 829, () => 图像_.查找文字(挑战炼狱, (string s) => s.Contains("挑战")).X != -1);
            if (!找到) throw new Exception("没能点击到炼狱旋涡");
            人物.输出("传送到: " + 目的地.名称);
            var 一次购买时长 = new TimeSpan(30, 0, 0);
            if (人物.自动购买炼狱时间 && 人物.剩余炼狱时长 < 一次购买时长)
            {
                var 剩余时长文本 = 图像_.识别文字(炼狱时间);
                var 时位置 = 剩余时长文本.IndexOf('时');
                int 时 = 30, 分 = 0;
                try
                {
                    if (时位置 != -1)
                    {
                        var 时文本 = 剩余时长文本.Substring(0, 时位置).Trim();
                        时 = int.Parse(时文本);
                        剩余时长文本 = 剩余时长文本.Substring(时位置 + 1);
                    }
                    var 分位置 = 剩余时长文本.IndexOf('分');
                    if (分位置 != -1)
                    {
                        var 分文本 = 剩余时长文本.Substring(0, 分位置).Trim();
                        分 = int.Parse(分文本);
                    }
                }
                catch
                {
                    时 = 30;
                    人物.输出("识别时间文本错误");
                }

                人物.输出("炼狱时间剩余: " + 时 + ":" + 分);

                人物.剩余炼狱时长 = new TimeSpan(时, 分, 0);

                if (人物.剩余炼狱时长 < 一次购买时长)
                {
                    人物.输出("炼狱时间不足30小时, 购买30小时炼狱时间");
                    // 购买炼狱时间
                    if (其他_.循环判断(() => 图像_.查找文字(购买炼狱时间, (string s) => s.Contains("时间")).X != -1))
                    {
                        键鼠_.左键单击(240, 200);
                        if (其他_.循环判断(() => 图像_.查找文字("购买", 购买按钮).X != -1))
                        {
                            键鼠_.左键单击(400, 150);
                            其他_.延迟(其他_.时长_1000);
                            键鼠_.左键单击(516, 58);
                            找到 = UI_.鼠标点击屏幕坐标(187, 829, () => 图像_.查找文字(挑战炼狱, (string s) => s.Contains("挑战")).X != -1);
                            if (!找到) throw new Exception("没能点击到炼狱旋涡");
                        }
                    }
                    人物.剩余炼狱时长 += 一次购买时长;
                }
            }
            人物.输出("开始挑战");
            键鼠_.左键单击(100, 200);
            return UI_.判断地图名("海岛");
        }
    }

    public class 马倌_ : Npc_
    {
        public 马倌_() : base(new 点及坐标_(new Point(465, 582), new 坐标_(184, 809)), 点属性_.传送_, "马倌", "马倌", 地图_.跃马大平原)
        {
            this.实现快速接近 = true;
        }

        public override 任务名_ 快速接近(挂机人物_ 人物)
        {
            var 地图 = UI_.获取当前地图();
            if (地图 == 地图_.跃马大平原)
            {
                var p = UI_.连续识别坐标();
                var 较近 = UI_.坐标点距离较近(p, this.位置.坐标, 4);
                if (较近)
                {
                    UI_.关闭今日挑战();
                    return 任务名_.已经到达指定地点_;
                }
            }
            UI_.去马倌.传送(人物, null);
            return 任务名_.已经到达指定地点_;
        }
        public bool 兑换一个马牌(挂机人物_ 人物)
        {
            UI_.鼠标点击屏幕坐标(new 坐标_(184, 805), () => 图像_.查找文字("骑兵竞技", 54, 211, 135, 233).X != -1);
            键鼠_.左键单击(93, 223);
            var 找到 = 其他_.循环判断(() => 图像_.查找文字("骑兵竞技入场", 40, 202, 152, 222).X != -1);
            if (找到)
            {
                键鼠_.左键单击(97, 171);
                找到 = 其他_.循环判断(() => 图像_.查找文字("兑换一匹", 58, 253, 135, 271).X != -1);
                if (找到)
                {
                    // 点击 购买
                    键鼠_.左键单击(96, 304);
                    其他_.延迟(其他_.时长_1000);
                    // 点击兑换
                    键鼠_.左键单击(96, 263);
                    其他_.延迟(其他_.时长_1000);
                    // 点击关闭
                    键鼠_.左键单击(518, 55);
                    其他_.延迟(其他_.时长_1000);
                    找到 = 其他_.循环判断(() => 图像_.查找文字("骑兵竞技入场", 40, 202, 152, 222).X == -1, 3);
                }
            }
            return 找到;
        }
        public bool 去骑兵竞技(挂机人物_ 人物)
        {
            UI_.鼠标点击屏幕坐标(new 坐标_(184, 805), () => 图像_.查找文字("骑兵竞技", 54, 211, 135, 233).X != -1);
            键鼠_.左键单击(93, 223);
            var 找到 = 其他_.循环判断(() => 图像_.查找文字("骑兵竞技入场", 40, 202, 152, 222).X != -1);
            if (找到)
            {
                键鼠_.左键单击(419, 213);
                其他_.延迟(其他_.时长_1000);
                键鼠_.左键单击(100, 212);
                找到 = UI_.判断地图名("骑兵竞技场");
            }
            return 找到;
        }
    }

    public class 声望老人_ : Npc_
    {
        public 声望老人_() : base(new 点及坐标_(new Point(447, 583), new 坐标_(165, 811)), 点属性_.传送_, "声望老人", "老者", 地图_.跃马大平原)
        {
            this.实现快速接近 = true;
        }

        public override 任务名_ 快速接近(挂机人物_ 人物)
        {
            var 地图 = UI_.获取当前地图();
            if (地图 == 地图_.跃马大平原)
            {
                var p = UI_.连续识别坐标();
                var 较近 = UI_.坐标点距离较近(p, this.位置.坐标, 4);
                if (较近)
                {
                    UI_.关闭今日挑战();
                    return 任务名_.已经到达指定地点_;
                }
            }
            UI_.去声望老人.传送(人物, null);
            return 任务名_.已经到达指定地点_;
        }
        private void 点击声望老人()
        {
            UI_.鼠标点击屏幕坐标(new 坐标_(163, 807), () => 图像_.查找文字("帮助矿工", 57, 150, 135, 172).X != -1);
        }
        public bool 去帮助矿工(挂机人物_ 人物)
        {
            this.点击声望老人();
            人物.输出("点击帮助矿工");
            键鼠_.左键单击(94, 159);
            var 找到 = UI_.判断地图名("东部矿区");
            if (!找到) return false;

            UI_.鼠标点击屏幕坐标(new 坐标_(52, 23), () => 图像_.查找文字("答应", 71, 152, 118, 171).X != -1);

            找到 = 其他_.循环判断(() => 图像_.查找文字("答应", 71, 152, 118, 171).X != -1, 5);
            if (!找到) throw new Exception("未能点击到NPC");
            键鼠_.左键单击(90, 162);
            var 找到矿洞 = UI_.判断地图名("矿洞", 5);
            if (找到矿洞)
            {
                人物.输出("已经到达矿洞, 可以开始战斗");
                return true;
            }
            人物.输出("帮助矿工错误");
            throw new Exception("帮助矿工错误");
        }
        public bool 去帮助奸商(挂机人物_ 人物)
        {
            this.点击声望老人();
            键鼠_.左键单击(244, 159);
            var 到达 = UI_.判断地图名("将军坟东五层");
            if (!到达) return false;
            其他_.延迟(其他_.时长_5000);
            UI_.鼠标点击屏幕坐标(new 坐标_(85, 197), () => 图像_.查找文字("声望任务", 203, 156, 280, 177).X != -1);
            键鼠_.左键单击(223, 166);
            var 找到 = 其他_.循环判断(() => 图像_.查找文字("答应", 71, 152, 118, 171).X != -1, 5);
            if (找到)
            {
                键鼠_.左键单击(95, 162);
                var 找到石窟 = UI_.判断地图名("石窟");
                if (找到石窟)
                {
                    其他_.延迟(其他_.时长_1000);
                    return true;
                }
            }
            return false;
        }

        public bool 去帮助将军(挂机人物_ 人物)
        {
            this.点击声望老人();
            键鼠_.左键单击(383, 159);
            其他_.延迟(其他_.时长_5000);
            var 到达 = UI_.识别坐标();
            var 目标 = new 坐标_(464, 539);
            if (Math.Abs(到达.X - 目标.X) > 5 || Math.Abs(到达.Y - 目标.Y) > 5)
            {
                return false;
            }
            UI_.鼠标点击屏幕坐标(new 坐标_(465, 537), () => 图像_.查找文字("答应", 71, 152, 118, 171).X != -1);
            键鼠_.左键单击(97, 160);
            var 找到 = UI_.判断地图名("吊桥");
            if (找到)
            {
                其他_.延迟(其他_.时长_1000);
                return true;
            }
            return false;
        }

    }

    public class 城主雕像_ : Npc_
    {
        public 城主雕像_() : base(new 点及坐标_(new Point(509, 611), new 坐标_(229, 868)), 点属性_.传送_, "城主雕像", "城主雕像", 地图_.跃马大平原)
        {
            this.实现快速接近 = true;
        }

        public override 任务名_ 快速接近(挂机人物_ 人物)
        {
            var 地图 = UI_.获取当前地图();
            if (地图 == 地图_.跃马大平原)
            {
                var p = UI_.连续识别坐标();
                var 较近 = UI_.坐标点距离较近(p, this.位置.坐标, 4);
                if (较近) return 任务名_.已经到达指定地点_;
                较近 = UI_.坐标点距离较近(p, 地图_.土城镖师.位置.坐标, 4);
                if (较近) return 任务名_.走到指定点附近_;
            }
            UI_.去拉镖活动.传送(人物, null);
            return 任务名_.走到指定点附近_;
        }

        public bool 找到我(挂机人物_ 人物)
        {
        // UI_.去拉镖活动.传送(人物, null);
        重试_:
            var i = 0;
            UI_.移动到地图内准确位置(this.位置.像素点);
            var 找到 = 其他_.循环判断(() => 其他_.点在有效范围内(UI_.计算屏幕坐标点位(UI_.连续识别坐标(), new 坐标_(229, 868), out var 方向)), 50);
            if (!找到) {
                i++;
                if (i < 2) goto 重试_;
                throw new Exception("没找到城主雕像");
            }
            键鼠_.左键单击(640, 400);
            return 找到;
        }
        public bool 去城堡正门(挂机人物_ 人物)
        {
            this.找到我(人物);
            UI_.鼠标点击屏幕坐标(new 坐标_(227, 866), () => 图像_.查找文字("城堡正门", 393, 190, 475, 210).X != -1);
            键鼠_.左键单击(431, 200);
            var 找到 = 其他_.循环判断(() => UI_.坐标点距离较近(UI_.连续识别坐标(), new 坐标_(212, 425), 10), 10);
            if (!找到) throw new Exception("未能到达城堡正门");
            其他_.延迟(其他_.时长_400);
            return 找到;
        }
    }

    public class 武馆教头_ : Npc_
    {
        public 武馆教头_() : base(new 点及坐标_(new Point(399, 603), new 坐标_(116, 852)), 点属性_.传送_, "武馆教头", "武馆教头", 地图_.跃马大平原)
        {
            this.实现快速接近 = true;
        }

        public override 任务名_ 快速接近(挂机人物_ 人物)
        {
            var 地图 = UI_.获取当前地图();
            if (地图 == 地图_.跃马大平原)
            {
                var p = UI_.连续识别坐标();
                var 较近 = UI_.坐标点距离较近(p, this.位置.坐标, 4);
                if (较近)
                {
                    UI_.关闭今日挑战();
                    return 任务名_.已经到达指定地点_;
                }
                较近 = UI_.坐标点距离较近(p, 地图_.声望老人.位置.坐标, 4);
                if (较近)
                {
                    UI_.关闭今日挑战();
                    return 任务名_.走到指定点附近_;
                }
            }
            UI_.去声望老人.传送(人物, null);
            return 任务名_.走到指定点附近_;
        }
        public bool 去论武任务(挂机人物_ 人物)
        {
            UI_.鼠标点击屏幕坐标(new 坐标_(114, 850), () => 图像_.查找文字("论武", 369, 156, 410, 177).X != -1);
            键鼠_.左键单击(386, 166);
            var 找到 = 其他_.循环判断(() =>
            {
                return 图像_.查找文字("开始挑战", 55, 149, 134, 171).X != -1;
            }, 2);
            if (找到)
            {
                键鼠_.左键单击(96, 160);
                var 结果 = UI_.判断地图名("比武室", 5);
                if (!结果)
                {
                    找到 = 其他_.循环判断(() =>
                    {
                        return 图像_.查找文字("开始挑战", 55, 149, 134, 171).X != -1;
                    }, 2);
                    if (!找到)
                    {
                        throw new Exception("论武挑战传送失败");
                    }
                    // 关闭对话框
                    键鼠_.左键单击(519, 55);
                    结果 = !其他_.循环判断(() =>
                    {
                        return 图像_.查找文字("开始挑战", 55, 149, 134, 171).X != -1;
                    }, 2);
                    // 可能是已经完成了, 点击关闭窗口
                    if (!结果) throw new Exception("关闭论武窗口失败");
                    return false;
                }
                return true;
            }
            throw new Exception("论武任务失败");
        }
    }

    public class 王城公告牌_ : Npc_
    {
        public 王城公告牌_() : base(new 点及坐标_(new Point(453, 583), new 坐标_(172, 805)), 点属性_.传送_, "王城公告牌", "江湖公告", 地图_.跃马大平原)
        {
            this.实现快速接近 = true;
        }

        public override 任务名_ 快速接近(挂机人物_ 人物)
        {
            UI_.去王城任务.传送(人物, null);
            return 任务名_.已经到达指定地点_;
        }
        public int 判断目前是任务几()
        {
            return 其他_.尝试编译带斜杠号数字的分子(图像_.识别文字(Rectangle.FromLTRB(894, 419, 944, 440)).Trim());
        }
        public bool 去王城任务(挂机人物_ 人物, 完成情况_ 继续任务)
        {
            UI_.打开今日挑战();
            var 做完了 = 任务积分是否满足(Rectangle.FromLTRB(740, 300, 780, 320));
            人物.输出("王城任务: " + 做完了.ToString());
            人物.任务关注怪物.Clear();
            if (做完了 < 27)
            {
                快速接近(人物);
                UI_.鼠标点击屏幕坐标(173, 804, () => 图像_.查找文字(664, 328, 827, 348, s => s.Contains("任务") && s.Contains("诏令")).X != -1);
                键鼠_.左键单击(763, 335);
                if (继续任务 == 完成情况_.有未领奖励)
                {
                    人物.输出("领取任务奖励");
                    其他_.延迟(其他_.时长_1000);
                    var 找到 = 其他_.循环判断(() => 图像_.查找文字("领取任务奖励", 667, 420, 782, 440).X != -1, 5);
                    if (!找到) return false;
                    人物.输出("领取任务奖励");
                    键鼠_.左键单击(720, 430);
                    其他_.延迟(其他_.时长_1000);
                    键鼠_.左键单击(640, 439);
                }
                if (其他_.循环判断(() => 图像_.查找文字("我要接任务", 667, 420, 765, 440).X != -1, 5))
                {
                    var 是任务几 = 判断目前是任务几();
                    人物.输出("是接受任务: " + (是任务几 + 1).ToString());
                    // 点击接受任务
                    人物.输出("点击接受任务");
                    键鼠_.左键单击(717, 430);
                    if (其他_.循环判断(() => 图像_.查找文字("接受任务", 59, 207, 131, 228).X != -1, 5))
                    {
                        键鼠_.左键单击(其他_.取中心(59, 207, 131, 228));
                        其他_.延迟(其他_.时长_400);
                        人物.输出("点击离开");
                        键鼠_.左键单击(517, 56);
                        其他_.延迟(其他_.时长_1000);
                    }
                    return true;
                }
                if (其他_.循环判断(() => 图像_.查找文字("明日再来", 667, 420, 765, 440).X != -1, 5))
                {
                    // 点击明日再来
                    键鼠_.左键单击(其他_.取中心(667, 420, 765, 440));
                    return true;
                }
            }
            else
            {
                UI_.关闭今日挑战();
            }
            return false;
        }

        public int 任务积分是否满足(Rectangle ret)
        {
            try
            {
                var 积分 = 图像_.识别文字(ret);
                return Convert.ToInt32(积分.Trim(), 10);
            }
            catch (Exception)
            {
                return 0;
            }
        }
        public enum 完成情况_
        {
            无_,
            有未领奖励,
            有未完成击杀,
        }

        public 完成情况_ 有未完成的任务()
        {
            var 王城任务 = 其他_.循环判断(() => 图像_.查找文字(Rectangle.FromLTRB(45, 36, 219, 61), (string s) => s.Contains("王城诏令") && !s.Contains("十")).X != -1, 5);
            if (王城任务)
            {
                var 内容 = 其他_.循环判断(() => 图像_.查找文字("江湖公告", 171, 67, 251, 89).X != -1, 5);
                if (内容)
                {
                    return 完成情况_.有未领奖励;
                }
                return 完成情况_.有未完成击杀;
            }
            return 完成情况_.无_;
        }
    }

    public class 老兵_ : 传送门_
    {
        public 老兵_() : base(new 点及坐标_(new Point(460, 585), new 坐标_(178, 813)), "老兵", "老兵", 地图_.跃马大平原,
            new List<地图_>  {
                地图_.魔隐村,
                地图_.铁血魔城,
                地图_.地下魔域一层,
                地图_.矿区入口,
                地图_.将军坟东一层,
                地图_.将军坟东二层,
                地图_.将军坟一层,
                地图_.机关支路一,
                地图_.地穴入口,
                地图_.环道,
                地图_.逆魔古刹一层,
                地图_.逆魔古刹二层,
                地图_.禁地之门,
                地图_.一手遮天,
                地图_.深海密道一层
        })
        {
            this.实现快速接近 = true;
        }

        public override 任务名_ 快速接近(挂机人物_ 人物)
        {
            var 地图 = UI_.获取当前地图();
            if (地图 == 地图_.跃马大平原)
            {
                var p = UI_.连续识别坐标();
                var 较近 = UI_.坐标点距离较近(p, this.位置.坐标, 5);
                if (较近)
                {
                    UI_.关闭今日挑战();
                    return 任务名_.已经到达指定地点_;
                }
                较近 = UI_.坐标点距离较近(p, 地图_.马倌.位置.坐标, 2);
                if (较近)
                {
                    UI_.关闭今日挑战();
                    return 任务名_.已经到达指定地点_;
                }
            }
            UI_.去马倌.传送(人物, null);
            return 任务名_.已经到达指定地点_;
        }
        public override bool 传送(挂机人物_ 人物, 地图_ 目的地)
        {
            if (!this.目标地图.Contains(目的地)) return false;

            UI_.鼠标点击屏幕坐标(177, 810, () => 图像_.查找文字("矿区入口", 55, 106, 136, 126).X != -1);

            人物.输出("传送到: " + 目的地.名称);
            switch (目的地.储存键)
            {
                case "矿区入口":
                    {
                        var p = 图像_.查找文字("矿区入口", 55, 106, 136, 126);
                        if (p.X != -1)
                        {
                            键鼠_.左键单击(97, 116);
                            return UI_.判断地图名(目的地.名称);
                        }
                        return false;
                    }
                case "将军坟东一层":
                case "将军坟一层":
                    {
                        var p = 图像_.查找文字("将军坟", 212, 106, 285, 126);
                        if (p.X != -1)
                        {
                            键鼠_.左键单击(242, 116);
                            var 名称 = 目的地.名称;
                            if (名称 == "将军坟东一层")
                            {
                                var 找到 = 其他_.循环判断(() => 图像_.查找文字("将军东", 57, 115, 141, 138).X != -1, 5);
                                if (找到)
                                {
                                    键鼠_.左键单击(97, 127);
                                    return UI_.判断地图名(目的地.名称);
                                }
                            }
                            else
                            {
                                var 找到 = 其他_.循环判断(() => 图像_.查找文字("将军坟北", 102, 115, 292, 138).X != -1, 5);
                                if (找到)
                                {
                                    键鼠_.左键单击(244, 127);
                                    return UI_.判断地图名(目的地.名称);
                                }
                            }
                        }
                        return false;
                    }
                case "机关支路一":
                    {
                        var p = 图像_.查找文字("机关洞", 357, 106, 425, 126);
                        if (p.X != -1)
                        {
                            键鼠_.左键单击(390, 127);
                            return UI_.判断地图名(目的地.名称);
                        }
                        return false;
                    }
                case "地穴入口":
                    {
                        var p = 图像_.查找文字("蛇魔谷", 504, 106, 571, 126);
                        if (p.X != -1)
                        {
                            键鼠_.左键单击(535, 127);
                            return UI_.判断地图名(目的地.名称);
                        }
                        return false;
                    }
                case "逆魔古刹一层":
                    {
                        var p = 图像_.查找文字("逆魔古刹", 55, 156, 136, 176);
                        if (p.X != -1)
                        {
                            键鼠_.左键单击(97, 166);
                            return UI_.判断地图名(目的地.名称);
                        }
                        return false;
                    }
                case "禁地之门":
                    {
                        var p = 图像_.查找文字("禁地洞穴", 203, 156, 285, 176);
                        if (p.X != -1)
                        {
                            键鼠_.左键单击(242, 166);
                            return UI_.判断地图名(目的地.名称);
                        }
                        return false;
                    }
                case "一手遮天":
                    {
                        var p = 图像_.查找文字("通天塔", 350, 156, 435, 176);
                        if (p.X != -1)
                        {
                            键鼠_.左键单击(390, 166);
                            return UI_.判断地图名(目的地.名称);
                        }
                        return false;
                    }
                case "逆魔古刹二层":
                case "环道":
                case "将军坟东二层":
                    {
                        var p = 图像_.查找文字("玄冰古道", 497, 156, 575, 176);
                        if (p.X != -1)
                        {
                            键鼠_.左键单击(535, 166);
                            var 名称 = 目的地.名称;
                            if (名称 == "逆魔古刹二层")
                            {
                                var 找到 = 其他_.循环判断(() => 图像_.查找文字("玄冰古道东", 47, 115, 145, 138).X != -1, 5);
                                if (找到)
                                {
                                    键鼠_.左键单击(97, 127);
                                    return UI_.判断地图名(目的地.名称);
                                }
                            }
                            else if (名称 == "环道")
                            {
                                var 找到 = 其他_.循环判断(() => 图像_.查找文字("玄冰古道西", 195, 115, 145, 292).X != -1, 5);
                                if (找到)
                                {
                                    键鼠_.左键单击(244, 127);
                                    return UI_.判断地图名(目的地.名称);
                                }
                            }
                            else
                            {
                                var 找到 = 其他_.循环判断(() => 图像_.查找文字("玄冰古道南", 340, 115, 438, 292).X != -1, 5);
                                if (找到)
                                {
                                    键鼠_.左键单击(386, 127);
                                    return UI_.判断地图名(目的地.名称);
                                }
                            }
                        }
                        return false;
                    }
                case "地下魔域一层":
                    {
                        var 找到 = 其他_.循环判断(() => 图像_.查找文字("地下魔域", 55, 206, 136, 226).X != -1, 5);
                        if (找到)
                        {
                            键鼠_.左键单击(97, 216);
                            return UI_.判断地图名(目的地.名称);
                        }
                        return false;
                    }
                case "魔隐村":
                    {
                        var 找到 = 其他_.循环判断(() => 图像_.查找文字("魔隐村", 203, 206, 285, 226).X != -1, 5);
                        if (找到)
                        {
                            键鼠_.左键单击(242, 216);
                            return UI_.判断地图名(目的地.名称);
                        }
                        return false;
                    }
                case "铁血魔城":
                    {
                        var 找到 = 其他_.循环判断(() => 图像_.查找文字("铁血魔城", 350, 206, 435, 226).X != -1, 5);
                        if (找到)
                        {
                            键鼠_.左键单击(390, 216);
                            return UI_.判断地图名(目的地.名称);
                        }
                        return false;
                    }
                case "深海密道一层":
                    {
                        var p = 其他_.循环判断(() => 图像_.查找文字("幽灵沉船", 55, 256, 136, 276).X != -1, 5);
                        if (p)
                        {
                            键鼠_.左键单击(97, 266);
                            var p2 = 其他_.循环判断(() => 图像_.查找文字("送我过去", 54, 128, 139, 151).X != -1, 5);
                            if (p2)
                            {
                                键鼠_.左键单击(95, 138);
                                // 幽海渡者
                                UI_.移动(UI_.移动方向_.下_, 10);
                                其他_.延迟(其他_.时长_1000);
                                UI_.点击当前NPC名称(() => 图像_.查找文字("进入幽灵沉船", 37, 130, 155, 152).X != -1);
                                键鼠_.左键单击(96, 139);
                                return UI_.判断地图名(目的地.名称);
                            }
                        }
                        return false;
                    }
                default:
                    return false;
            }
        }
    }
    public class 账号安全员_ : 传送_
    {
        public 账号安全员_() : this(new 点及坐标_(new Point(531, 417), new 坐标_(17, 18)),
            "小黑屋去跃马大平原", "账号安全",
            地图_.小黑屋,
            new List<地图_> { 地图_.跃马大平原 })
        {
        }
        public 账号安全员_(点及坐标_ 位置, string 名称, string 识别名, 地图_ 所在地图, List<地图_> 目标地图)
            : base(位置, 点属性_.传送门_, 名称, 识别名, 所在地图, 目标地图)
        { }

        public bool 找到我(挂机人物_ 人物)
        {
            var 到达 = 其他_.点在有效范围内(UI_.计算屏幕坐标点位(UI_.连续识别坐标(), new 坐标_(16, 16), out var 方向));
            if (!到达) 到达 = UI_.移动到地图内准确位置(this.位置.像素点);
            if (!到达)
            {
                var i = 0;
                do
                {
                    i++;
                    UI_.移动(UI_.根据位置判断方向(UI_.连续识别坐标(), this.位置.坐标, out var t), t);
                    到达 = 其他_.点在有效范围内(UI_.计算屏幕坐标点位(UI_.连续识别坐标(), new 坐标_(16, 16), out var _));
                }
                while (!到达 && i < 10);
                if (i >= 10) throw new Exception("没找到账号安全员");
            }
            return true;
        }

        public override bool 传送(挂机人物_ 人物, 地图_ _)
        {
            this.找到我(人物);
            var 点击到 = UI_.鼠标点击屏幕坐标(16, 16, () => 图像_.查找文字("解冻账号", 56, 156, 134, 177).X != -1);
            键鼠_.左键单击(100, 166);
            var 请输入 = 其他_.循环判断(() => 图像_.查找文字(548, 335, 715, 358, (string s) => s.Contains("请输")).X != -1, 5);
            if (!请输入) throw new Exception("游戏卡死");
            键鼠_.左键单击(598, 385);
            其他_.延迟(其他_.时长_1000);
            键鼠_.输入文本(人物.二级密码);
            其他_.延迟(其他_.时长_1000);
            var 已经输入 = 其他_.循环判断(() => 图像_.查找文字(563, 369, 717, 400, (string s) =>
            {
                人物.输出("识别出的: " + s);
                return s.Contains(人物.二级密码);
            }).X != -1, 10);
            if (!已经输入) throw new Exception("游戏卡死");
            键鼠_.左键单击(524, 439);
            var R = UI_.判断地图名("跃马大平原", 5);
            if (!R) throw new Exception("二级密码错误");
            其他_.延迟(其他_.时长_5000);
            return R;
        }
    }

    public class 进入炼狱魔境_ : 炼狱光门_
    {
        public Rectangle 进入炼狱魔境 = Rectangle.FromLTRB(38, 150, 153, 172);
        public 进入炼狱魔境_() : base(new 点及坐标_(new Point(686, 440), new 坐标_(123, 90)), "进入炼狱魔境", "传送门", 地图_.海岛, new List<地图_> { 地图_.炼狱魔境一层 })
        { }
        public override bool 传送(挂机人物_ 人物, 地图_ 目标)
        {
            if (目标 != 地图_.炼狱魔境一层) return false;
            var 点击到 = UI_.鼠标点击屏幕坐标(125, 89, () => 图像_.查找文字("进入炼狱魔境", 进入炼狱魔境).X != -1);
            if (!点击到) return false;
            键鼠_.左键单击(88, 160);
            return UI_.判断地图名("炼狱魔境一层");
        }
    }

    public class 魔隐村妖力士_ : 传送_
    {
        public 魔隐村妖力士_() : base(new 点及坐标_(new Point(633, 408), new 坐标_(185, 127)), "魔隐村妖力士", "妖力士", 地图_.魔隐村, new List<地图_> { 地图_.魔界角斗场 })
        { }

        public override bool 传送(挂机人物_ 人物, 地图_ 目标)
        {
            if (目标 != 地图_.魔界角斗场) return false;
            UI_.鼠标点击屏幕坐标(187, 127, () => 图像_.查找文字("进入角斗场", 48, 197, 143, 220).X != -1);
            键鼠_.左键单击(其他_.取中心(48, 197, 143, 220));
            var 成功 = UI_.判断地图名("魔界角斗场");
            if (!成功) throw new Exception("进入魔界角斗场失败");
            return 成功;
        }
    }

    public class 魔境一去魔境二_ : 门点_
    {
        public 魔境一去魔境二_() : base(new 点及坐标_(new Point(596, 412), new 坐标_(227, 172)), "魔境一去魔境二", "", 地图_.炼狱魔境一层, new List<地图_> { 地图_.炼狱魔境二层 })
        { }

        public override bool 传送(挂机人物_ 人物, 地图_ 目标)
        {
            if (目标 != 地图_.炼狱魔境二层) return false;
            var p = UI_.连续识别坐标();
            UI_.切换到走路();
            var 到达 = 其他_.循环判断(() =>
            {
                UI_.移动(UI_.根据位置判断方向(p, new 坐标_(224, 174), out int t, false), t);
                其他_.延迟(其他_.时长_100);
                p = UI_.连续识别坐标();
                return UI_.判断地图名("炼狱魔境二层", 1);
            }, 10, 200);
            UI_.切换到跑路();
            return 到达;
        }
    }

    public class 魔境二去魔境三_ : 门点_
    {
        public 魔境二去魔境三_() : base(new 点及坐标_(new Point(401, 340), new 坐标_(119, 93)), "魔境二去魔境三_", "", 地图_.炼狱魔境二层, new List<地图_> { 地图_.炼狱魔境三层 })
        { }
    }

    public class 魔境一去魔境三_ : 门点_
    {
        public 魔境一去魔境三_() : base(new 点及坐标_(new Point(401, 340), new 坐标_(119, 93)), "魔境一去魔境三", "", 地图_.炼狱魔境一层, new List<地图_> { 地图_.炼狱魔境三层 })
        { }

        public override bool 传送(挂机人物_ 人物, 地图_ 目标)
        {
            if (目标 != 地图_.炼狱魔境三层) return false;
            var p = UI_.连续识别坐标();
            UI_.切换到走路();
            var 到达 = 其他_.循环判断(() =>
            {
                UI_.移动(UI_.根据位置判断方向(p, new 坐标_(120, 94), out int t, false), t);
                其他_.延迟(其他_.时长_100);
                p = UI_.连续识别坐标();
                return UI_.判断地图名("炼狱魔境三层", 1);
            }, 10, 200);
            UI_.切换到跑路();
            return 到达;
        }
    }

    public class 物品及数量_
    {
        public 物品及数量_(string 名称, int 数量)
        {
            this.名称 = 名称;
            this.数量 = 数量;
        }
        public string 名称 { get; private set; }
        public int 数量 { get; private set; }
    }

    public class 土城镖师_ : Npc_
    {
        public 土城镖师_() : base(new 点及坐标_(new Point(511, 599), new 坐标_(230, 846)), 点属性_.传送_, "土城镖师", "土城镖师", 地图_.跃马大平原)
        {
            this.实现快速接近 = true;
        }
        public override 任务名_ 快速接近(挂机人物_ 人物)
        {
            var 地图 = UI_.获取当前地图();
            if (地图 == 地图_.跃马大平原)
            {
                var p = UI_.连续识别坐标();
                var 较近 = UI_.坐标点距离较近(p, this.位置.坐标, 5);
                if (较近)
                {
                    UI_.关闭今日挑战();
                    return 任务名_.已经到达指定地点_;
                }
            }
            UI_.去拉镖活动.传送(人物, null);
            return 任务名_.已经到达指定地点_;
        }
        public bool 金币镖车(挂机人物_ 人物)
        {
            UI_.鼠标点击屏幕坐标(230, 843, () => 图像_.查找文字("金币镖车", 56, 163, 136, 182).X != -1);
            键鼠_.左键单击(其他_.取中心(56, 163, 136, 182));
            var 找到 = 其他_.循环判断(() => 图像_.查找文字("押送镖车", 56, 209, 135, 229).X != -1);
            if (找到)
            {
                键鼠_.左键单击(其他_.取中心(56, 209, 135, 229));
                其他_.延迟(其他_.时长_800);
                var i = 0;
                do
                {
                    找到 = 战斗_.找到自己的镖车(人物);
                    if (找到) break;
                    i++;
                    其他_.延迟(其他_.时长_400);
                }
                while (i < 4);
                // 关闭押镖窗口
                键鼠_.左键单击(516, 55);
                return 找到;
            }
            throw new Exception("押送镖车失败");
        }
    }

    public class 土城去沙城仓库_ : 门点_
    {
        public 土城去沙城仓库_() : base(new 点及坐标_(new Point(454, 383), new 坐标_(173, 398)), "土城去沙城仓库_", "", 地图_.跃马大平原, new List<地图_> { 地图_.沙城仓库 })
        {
            this.实现快速接近 = true;
        }
        public override 任务名_ 快速接近(挂机人物_ 人物)
        {
            var 地图 = UI_.获取当前地图();
            if (地图 == 地图_.跃马大平原)
            {
                var p = UI_.连续识别坐标();
                var 较近 = UI_.坐标点距离较近(p, this.位置.坐标, 5);
                if (较近) return 任务名_.已经到达指定地点_;
                较近 = UI_.坐标点距离较近(p, new 坐标_(454, 383), 100);
                if (较近) return 任务名_.走到指定点附近_;
            }
            var 到达 = 地图_.城主雕像.去城堡正门(人物);
            if (到达) return 任务名_.走到指定点附近_;
            return 任务名_.随机到点附近_;
        }

        public override bool 传送(挂机人物_ 人物, 地图_ 目标)
        {
            if (目标 != 地图_.沙城仓库) return false;
            var p = UI_.连续识别坐标();
            UI_.切换到走路();
            var 到达 = 其他_.循环判断(() =>
            {
                UI_.移动(UI_.根据位置判断方向(p, new 坐标_(172, 396), out int t, false), t);
                其他_.延迟(其他_.时长_100);
                p = UI_.连续识别坐标();
                return UI_.判断地图名("仓库", 1);
            }, 10, 200);
            UI_.切换到跑路();
            return 到达;
        }
    }

    public class 仓库_ : Npc_
    {
        public 仓库_(点及坐标_ 位置, string 名称, string 识别名, 地图_ 所在地图)
            : base(位置, 点属性_.仓库_, 名称, 识别名, 所在地图)
        { }
    }
    public class 海岛仓库_ : 仓库_
    {
        public 坐标_ 点击坐标 { get; set; }
        public 海岛仓库_() : base(new 点及坐标_(new Point(640, 433), new 坐标_(112, 88)), "海岛仓库管理员", "仓库管理员", 地图_.海岛)
        {
            点击坐标 = new 坐标_(111, 87);
        }
        public 海岛仓库_(点及坐标_ 位置, string 名称, string 识别名, 地图_ 所在地图, 坐标_ 点击坐标) : base(位置, 名称, 识别名, 所在地图)
        { this.点击坐标 = 点击坐标; }

        public bool 一键取药(挂机人物_ 人物, int 数量, out int 库存)
        {
            var 点击成功 = UI_.鼠标点击屏幕坐标(this.点击坐标, () => 图像_.查找文字("药水仓库", 204, 152, 282, 173).X != -1 || 图像_.查找文字("立即设置", 55, 136, 136, 158).X != -1);
            if (!点击成功) throw new Exception("点击仓库管理员失败");
            if (图像_.查找文字("立即设置", 55, 136, 136, 158).X != -1)
            {
                // 没有二级密码的
                键鼠_.左键单击(其他_.取中心(249, 135, 326, 157));
                其他_.延迟(其他_.时长_1000);
            }
            键鼠_.左键单击(240, 164);
            库存 = 查看治疗药水库存();
            人物.输出("当前仓库内有\"治疗药水\": " + 库存.ToString());
            if (库存 != -1 && 数量 >= 库存)
            {
                数量 = 库存;
            }
            点击成功 = 其他_.循环判断(() => 图像_.查找文字("取出", 58, 172, 96, 192).X != -1, 2);
            if (!点击成功) throw new Exception("没发现取出");
            键鼠_.左键单击(240, 164);
            其他_.延迟(其他_.时长_1000);
            键鼠_.左键单击(76, 183);
            其他_.延迟(其他_.时长_1000);
            // 点击文本框
            键鼠_.左键单击(610, 387);
            其他_.延迟(其他_.时长_2000);
            键鼠_.输入文本(数量.ToString());
            var 已经输入 = 其他_.循环判断(() => 图像_.查找文字(563, 369, 717, 400, (string s) => s.Contains(数量.ToString())).X != -1, 10);
            if (!已经输入) throw new Exception("取出错误");
            点击成功 = 其他_.循环判断(() => 图像_.查找文字("确定", 496, 428, 545, 450).X != -1, 2);
            if (!点击成功) throw new Exception("没发确定");
            键鼠_.左键单击(523, 440);
            其他_.延迟(其他_.时长_1000);

            点击成功 = !其他_.循环判断(() => 图像_.查找文字(339, 60, 493, 85, (string s) => s.Contains("请整理")).X != -1, 2, 500);
            点击成功 = !其他_.循环判断(() => 图像_.查找文字(33, 60, 179, 85, (string s) => s.Contains("如果我")).X != -1, 2, 500);

            键鼠_.左键单击(519, 57);
            return 点击成功;
        }

        private int 查看治疗药水库存()
        {
            var 内容 = "";
            其他_.循环判断(() =>
            {
                内容 = 图像_.识别文字(Rectangle.FromLTRB(50, 143, 153, 163)).Trim();
                return 内容 != "";
            }, 2);
            return 其他_.尝试编译带斜杠号数字的分子(内容);
        }
        public bool 一键存药(挂机人物_ 人物, out int 库存)
        {
            var 点击成功 = UI_.鼠标点击屏幕坐标(this.点击坐标, () => 图像_.查找文字("药水仓库", 204, 152, 282, 173).X != -1 || 图像_.查找文字("立即设置", 55, 136, 136, 158).X != -1);
            if (!点击成功) throw new Exception("点击仓库管理员失败");
            if (图像_.查找文字("立即设置", 55, 136, 136, 158).X != -1)
            {
                // 没有二级密码的
                键鼠_.左键单击(其他_.取中心(249, 135, 326, 157));
                其他_.延迟(其他_.时长_1000);
            }

            键鼠_.左键单击(240, 164);
            库存 = 查看治疗药水库存();
            人物.输出("当前仓库内有\"治疗药水\": " + 库存.ToString());

            其他_.延迟(其他_.时长_1000);
            点击成功 = 其他_.循环判断(() => 图像_.查找文字("一键存入", 358, 62, 441, 84).X != -1, 2);
            if (!点击成功) throw new Exception("点击一键存入失败");
            键鼠_.左键单击(401, 73);
            其他_.延迟(其他_.时长_1000);
            点击成功 = 其他_.循环判断(() => 图像_.查找文字("返回", 74, 107, 118, 128).X != -1, 2);
            if (!点击成功)
            {
                点击成功 = 其他_.循环判断(() => 图像_.查找文字("药水存放失败", 32, 56, 146, 83).X != -1, 2);
                if (!点击成功) throw new Exception("药水存放失败");
            }
            键鼠_.左键单击(517, 57);
            return true;
        }
    }
    public class 沙城仓库_ : 海岛仓库_
    {
        public 沙城仓库_() : base(new 点及坐标_(new Point(548, 525), new 坐标_(7, 14)), "沙城仓库管理员", "管理员", 地图_.沙城仓库, new 坐标_(4, 11))
        { }
    }
    public class 商人_ : Npc_
    {
        public 商人_(点及坐标_ 位置, string 名称, string 识别名, 地图_ 所在地图)
            : base(位置, 点属性_.商人_, 名称, 识别名, 所在地图) { }

        public virtual bool 购买(挂机人物_ 人物, List<物品及数量_> 物品名及数量)
        {
            throw new Exception("未实现");
        }
    }

    public class 海岛流浪商人_ : 商人_
    {
        public 海岛流浪商人_() : base(new 点及坐标_(new Point(637, 420), new 坐标_(112, 82)), "沙城药商", "流浪商人", 地图_.海岛)
        { }
        private bool 靠近我()
        {
            var 地图 = UI_.获取当前地图();
            if (地图 != 地图_.海岛) throw new Exception("当前不在海岛地图");
            var p = UI_.连续识别坐标();
            while (!UI_.坐标点距离较近(p, this.位置.坐标, 6))
            {
                UI_.移动(UI_.根据位置判断方向(p, this.位置.坐标, out var t, UI_.是跑步()), t);
                p = UI_.连续识别坐标();
            }
            return true;
        }

        public override bool 购买(挂机人物_ 人物, List<物品及数量_> 物品名及数量)
        {
            var 名称组 = 物品名及数量.Select((物品及数量_ a) => a.名称).ToArray();
            var 数量组 = 物品名及数量.Select((物品及数量_ a) => a.数量).ToArray();
            return 购买(人物, 名称组, 数量组);
        }
        private bool 购买(挂机人物_ 人物, string[] 名称组, int[] 数量组)
        {
            this.靠近我();
            var 点击成功 = UI_.鼠标点击屏幕坐标(111, 79, () => 图像_.查找文字("购入物品", 58, 105, 134, 125).X != -1);
            if (!点击成功) throw new Exception("点击海岛流浪商人失败");

            var 查看次数 = 0;
            List<string> 完成购买 = new List<string>();
        开始购买_:
            // 点击购入药品
            其他_.延迟(其他_.时长_1000);
            键鼠_.左键单击(97, 117);

            点击成功 = 其他_.循环判断(() => 图像_.查找文字("物品列表", 86, 330, 164, 354).X != -1, 2);
            if (!点击成功) throw new Exception("没发现物品列表");

            人物.输出("需要购买: " + string.Join(", ", 名称组));
            TextBlock[] 上一组内容 = null;
            for (var n = 0; n < 10; n++)
            {
                if (完成购买.Count == 名称组.Length)
                {
                    人物.输出("已经购买完毕");
                    其他_.延迟(其他_.时长_2000);
                    goto 结束购买_;
                }
                var 识别范围 = Rectangle.FromLTRB(60, 352, 175, 633);
                var 当前页物品 = 图像_.查找文字(识别范围).ToArray();

                if (上一组内容 == null)
                {
                    上一组内容 = 当前页物品;
                }
                else
                {
                    if (上一组内容.Select(a => a.Text).SequenceEqual(当前页物品.Select(b => b.Text)))
                    {
                        // 到最后了 重新 找一次;
                        查看次数++;
                        if (查看次数 < 2)
                        {
                            goto 开始购买_;
                        }
                        其他_.延迟(其他_.时长_1000 * 60 * 5);
                        goto 开始购买_;
                    }
                    上一组内容 = 当前页物品;
                }

                for (var i = 0; i < 名称组.Length; i++)
                {
                    var 当前 = 名称组[i];
                    var 存在 = 当前页物品.FirstOrDefault(a => !完成购买.Contains(当前) && a.Text == 当前);
                    if (存在 != null)
                    {
                        var p = new Point(识别范围.X + 存在.BoxPoints[0].X, 识别范围.Y + 存在.BoxPoints[0].Y);
                        键鼠_.左键单击(p.X + 40, p.Y + 10);
                        var 分组数量 = 0;
                        var 当前数量 = 数量组[i];
                        for (int ii = 0; ii < 当前数量; ii++)
                        {
                            if (分组数量 == 10)
                            {
                                分组数量 = 0;
                                if (其他_.循环判断(() =>
                                {
                                    var 当前项范围 = Rectangle.FromLTRB(识别范围.X, 识别范围.Y + 存在.BoxPoints[0].Y, 识别范围.X + 识别范围.Width, 识别范围.Y + 存在.BoxPoints[0].Y + 40);
                                    var 确认商品名 = 图像_.识别文字(当前项范围);
                                    return 确认商品名 == 当前;
                                }))
                                {
                                    键鼠_.左键单击(p.X + 40, p.Y + 10);
                                    其他_.延迟(其他_.时长_200);
                                    键鼠_.左键单击(92, 670);
                                }
                                else
                                {
                                    数量组[i] = 数量组[i] - ii;
                                    goto 开始购买_;
                                }
                            }
                            else
                            {
                                分组数量++;
                                键鼠_.左键单击(92, 670);
                                其他_.延迟(其他_.时长_200);
                            }
                        }
                        人物.输出("购买: " + 存在.Text + 当前数量.ToString() + "个");
                        if (!完成购买.Contains(当前))
                        {
                            完成购买.Add(当前);
                        }
                        if (完成购买.Count == 名称组.Length)
                        {
                            人物.输出("已经购买完毕");
                            其他_.延迟(其他_.时长_2000);
                            goto 结束购买_;
                        }
                        goto 开始购买_;
                    }
                }
                人物.输出("向下翻页");
                其他_.延迟(其他_.时长_1000);
                键鼠_.移动鼠标(406, 470);
                其他_.延迟(其他_.时长_400);
                键鼠_.滚动滚轮(-2);
                其他_.延迟(其他_.时长_2000);
            }
        结束购买_:
            点击成功 = 其他_.循环判断(() => 图像_.查找文字("物品列表", 86, 330, 164, 354).X != -1, 2);
            键鼠_.左键单击(518, 56);
            人物.输出("关闭购买窗口");
            其他_.延迟(其他_.时长_1000);
            return 点击成功;
        }
    }

    public class 沙城药商_ : 商人_
    {
        public Dictionary<string, int> 物品位置 { get; private set; }
        public 沙城药商_() : base(new 点及坐标_(new Point(495, 379), new 坐标_(214, 389)), "沙城药商", "药商", 地图_.跃马大平原)
        {
            this.实现快速接近 = true;
            this.物品位置 = new Dictionary<string, int>();
        }
        public override 任务名_ 快速接近(挂机人物_ 人物)
        {
            var 地图 = UI_.获取当前地图();
            if (地图 == 地图_.跃马大平原)
            {
                var p = UI_.连续识别坐标();
                var 较近 = UI_.坐标点距离较近(p, this.位置.坐标, 5);
                if (较近) return 任务名_.已经到达指定地点_;
                较近 = UI_.坐标点距离较近(p, new 坐标_(454, 383), 100);
                if (较近) return 任务名_.走到指定点附近_;
            }
            var 到达 = 地图_.城主雕像.去城堡正门(人物);
            if (到达) return 任务名_.走到指定点附近_;
            return 任务名_.随机到点附近_;
        }
        public override bool 购买(挂机人物_ 人物, List<物品及数量_> 物品名及数量)
        {
            var 名称组 = 物品名及数量.Select((物品及数量_ a) => a.名称).ToArray();
            var 数量组 = 物品名及数量.Select((物品及数量_ a) => a.数量).ToArray();
            return 购买(人物, 名称组, 数量组);
        }

        private bool 购买(挂机人物_ 人物, string[] 名称组, int[] 数量组)
        {
            var 点击成功 = UI_.鼠标点击屏幕坐标(213, 387, () => 图像_.查找文字("购入药品", 58, 128, 134, 148).X != -1);
            if (!点击成功) throw new Exception("点击药商失败");
            List<string> 完成购买 = new List<string>();
        开始购买_:
            其他_.延迟(其他_.时长_800);
            var 有返回 = 其他_.循环判断(() => 图像_.查找文字("返回", 31, 196, 74, 233).X != -1, 2);
            if (有返回)
            {
                键鼠_.左键单击(其他_.取中心(31, 196, 74, 233));
                其他_.延迟(其他_.时长_1000);
            }
            键鼠_.左键单击(95, 138);
            点击成功 = 其他_.循环判断(() => 图像_.查找文字("物品列表", 86, 330, 164, 354).X != -1, 2);
            if (!点击成功) throw new Exception("没发现物品列表");

            人物.输出("需要购买: " + string.Join(", ", 名称组));
            TextBlock[] 上一组内容 = null;
            for (var n = 0; n < 10 ; n++)
            {
                if (完成购买.Count == 名称组.Length)
                {
                    人物.输出("已经购买完毕");
                    其他_.延迟(其他_.时长_1000);
                    goto 结束购买_;
                }
                var 识别范围 = Rectangle.FromLTRB(60, 352, 175, 633);
                var 当前页物品 = 图像_.查找文字(识别范围).ToArray();

                if (上一组内容 == null)
                {
                    上一组内容 = 当前页物品;
                }
                else
                {
                    if (上一组内容.Select(a => a.Text).SequenceEqual(当前页物品.Select(b => b.Text)))
                    {
                        goto 开始购买_;
                    }
                    上一组内容 = 当前页物品;
                }

                for (var i = 0; i < 名称组.Length; i++)
                {
                    var 当前 = 名称组[i];
                    var 存在 = 当前页物品.FirstOrDefault(a => !完成购买.Contains(当前) && a.Text == 当前);
                    if (存在 != null)
                    {
                        var p = new Point(识别范围.X + 存在.BoxPoints[0].X, 识别范围.Y + 存在.BoxPoints[0].Y);
                        键鼠_.左键单击(p.X + 40, p.Y + 10);
                        var 当前数量 = 数量组[i];
                        for (int ii = 0; ii < 当前数量; ii+=24)
                        {
                            if (其他_.循环判断(() =>
                            {
                                var 当前项范围 = Rectangle.FromLTRB(识别范围.X, 识别范围.Y + 存在.BoxPoints[0].Y, 识别范围.X + 识别范围.Width, 识别范围.Y + 存在.BoxPoints[0].Y + 40);
                                var 确认商品名 = 图像_.识别文字(当前项范围);
                                return 确认商品名 == 当前;
                            }, 10, 200))
                            {
                                for (var 购买24次 = 0; 购买24次 < 24; 购买24次++) {
                                    键鼠_.左键单击(92, 670);
                                    其他_.延迟(其他_.时长_100);
                                }
                            }
                        }
                        人物.输出("购买: " + 存在.Text +  当前数量.ToString() + "个");
                        if (!完成购买.Contains(当前))
                        {
                            完成购买.Add(当前);
                        }
                        if (完成购买.Count == 名称组.Length)
                        {
                            人物.输出("已经购买完毕");
                            其他_.延迟(其他_.时长_1000);
                            goto 结束购买_;
                        }
                        goto 开始购买_;
                    }
                }
                向下翻页();
            }
        结束购买_:
            点击成功 = 其他_.循环判断(() => 图像_.查找文字("物品列表", 86, 330, 164, 354).X != -1, 2);
            键鼠_.左键单击(518, 56);
            人物.输出("关闭购买窗口");
            其他_.延迟(其他_.时长_1000);
            return 点击成功;

            void 向下翻页()
            {
                人物.输出("向下翻页");
                其他_.延迟(其他_.时长_600);
                键鼠_.移动鼠标(406, 470);
                其他_.延迟(其他_.时长_400);
                键鼠_.滚动滚轮(-2);
                其他_.延迟(其他_.时长_1000);
            }
        }
    }

    public class 土城铁匠_ : 商人_
    {
        public 土城铁匠_() : base(new 点及坐标_(new Point(488, 587), new 坐标_(209, 819)), "土城铁匠", "铁匠", 地图_.跃马大平原)
        {
            this.实现快速接近 = true;
        }
        public override 任务名_ 快速接近(挂机人物_ 人物)
        {
            var 地图 = UI_.获取当前地图();
            if (地图 == 地图_.跃马大平原)
            {
                var p = UI_.连续识别坐标();
                var 较近 = UI_.坐标点距离较近(p, this.位置.坐标, 5);
                if (较近)
                {
                    UI_.关闭今日挑战();
                    return 任务名_.已经到达指定地点_;
                }
                较近 = UI_.坐标点距离较近(p, 地图_.炼狱旋涡.位置.坐标, 10);
                if (较近)
                {
                    UI_.关闭今日挑战();
                    return 任务名_.走到指定点附近_;
                }
            }
            UI_.去炼狱旋涡.传送(人物, null);
            return 任务名_.走到指定点附近_;
        }
        public override bool 购买(挂机人物_ 人物, List<物品及数量_> 物品名及数量)
        {
            return 购买(人物, 物品名及数量.ToArray());
        }

        public bool 购买(挂机人物_ 人物, 物品及数量_[] 物品名及数量)
        {
            var 名称组 = 物品名及数量.Select((物品及数量_ a) => a.名称).ToArray();
            var 数量组 = 物品名及数量.Select((物品及数量_ a) => a.数量).ToArray();
            return 购买(人物, 名称组, 数量组);
        }

        private bool 购买(挂机人物_ 人物, string[] 名称组, int[] 数量组)
        {
            // UI_.点击当前NPC名称();
            var 找到 = UI_.鼠标点击屏幕坐标(206, 816, () => 图像_.查找文字("购入武器", 56, 105, 134, 126).X != -1);
            if (!找到) throw new Exception("点击铁匠失败");

            // 点击购入武器
            键鼠_.左键单击(95, 117);
            var 查看次数 = 0;
            List<string> 完成购买 = new List<string>();
        开始购买_:
            找到 = 其他_.循环判断(() => 图像_.查找文字("商品清单", 59, 190, 133, 211).X != -1, 2);
            // 点击商品清单
            键鼠_.左键单击(95, 200);
            if (找到)
            {
                人物.输出("需要购买的物品: " + string.Join(", ", 名称组));
                TextBlock[] 上一组内容 = null;
                for (var n = 0; n < 10; n++)
                {
                    if (完成购买.Count == 名称组.Length)
                    {
                        人物.输出("已经购买完毕");
                        其他_.延迟(其他_.时长_2000);
                        goto 结束购买_;
                    }
                    var 识别范围 = Rectangle.FromLTRB(60, 352, 175, 633);
                    var 当前页物品 = 图像_.查找文字(识别范围).ToArray();
                    if (上一组内容 == null)
                    {
                        上一组内容 = 当前页物品;
                    }
                    else
                    {
                        if (上一组内容.Select(a => a.Text).SequenceEqual(当前页物品.Select(b => b.Text)))
                        {
                            // 到最后了 重新 找一次;
                            查看次数++;
                            if (查看次数 < 2) goto 开始购买_;
                            goto 结束购买_;
                        }
                        上一组内容 = 当前页物品;
                    }

                    for (var i = 0; i < 名称组.Length; i++)
                    {
                        var 当前 = 名称组[i];
                        var 存在 = 当前页物品.FirstOrDefault(a => !完成购买.Contains(当前) && a.Text == 当前);
                        if (存在 != null)
                        {
                            var p = new Point(识别范围.X + 存在.BoxPoints[0].X, 识别范围.Y + 存在.BoxPoints[0].Y);
                            var 当前项范围 = Rectangle.FromLTRB(识别范围.X, 识别范围.Y + 存在.BoxPoints[0].Y, 识别范围.X + 识别范围.Width, 识别范围.Y + 存在.BoxPoints[0].Y + 40);
                            var 确认商品名 = 图像_.识别文字(当前项范围);
                            人物.输出("确认商品名: " + 确认商品名);
                            键鼠_.左键单击(p.X + 20, p.Y + 20);
                            其他_.延迟(其他_.时长_400);

                            找到 = 其他_.循环判断(() => 图像_.查找文字("确认", 68, 660, 116, 682).X != -1, 2);
                            if (!找到) return false;
                            键鼠_.左键单击(92, 670);
                            其他_.延迟(其他_.时长_2000);
                            var 当前数量 = 数量组[i];
                            for (int ii = 0; ii < 当前数量; ii++)
                            {
                                人物.输出("选择第一个物品");
                                找到 = 其他_.循环判断(() => 图像_.查找文字("商品清单", 59, 190, 133, 211).X != -1, 2);
                                if (!找到) return false;
                                键鼠_.左键单击(69, 371);
                                人物.输出("点击确定");
                                找到 = 其他_.循环判断(() => 图像_.查找文字("确认", 68, 660, 116, 682).X != -1, 2);
                                if (!找到) return false;
                                键鼠_.左键单击(92, 670);
                            }
                            人物.输出("购买: " + 存在.Text +  当前数量.ToString() + "个");
                            if (!完成购买.Contains(当前))
                            {
                                完成购买.Add(当前);
                            }
                            if (完成购买.Count == 名称组.Length)
                            {
                                人物.输出("已经购买完毕");
                                其他_.延迟(其他_.时长_2000);
                                goto 结束购买_;
                            }
                            goto 开始购买_;
                        }
                    }
                    人物.输出("向下翻页");
                    其他_.延迟(其他_.时长_1000);
                    键鼠_.移动鼠标(406, 470);
                    其他_.延迟(其他_.时长_400);
                    键鼠_.滚动滚轮(-30);
                    其他_.延迟(其他_.时长_2000);
                }
            结束购买_:
                找到 = 其他_.循环判断(() => 图像_.查找文字("商品清单", 59, 190, 133, 211).X != -1, 2);
                if (!找到) return false;
                键鼠_.左键单击(574, 54);
                人物.输出("关闭购买窗口");
                其他_.延迟(其他_.时长_1000);
                return true;
            }
            return false;
        }
    }

    public class 怪物_
    {
        public 怪物_(string 名称, 怪物属性_ 属性, byte 等级)
        {
            this.名称 = 名称;
            this.属性 = 属性;
            this.等级 = 等级;
            this.位置 = 坐标_.零值();
        }
        public 怪物_(string 名称, 怪物属性_ 属性, byte 等级, 坐标_ 位置)
        {
            this.名称 = 名称;
            this.属性 = 属性;
            this.等级 = 等级;
            this.位置 = 位置;
        }
        public string 名称 { get; private set; }
        public 怪物属性_ 属性 { get; private set; }
        public byte 等级 { get; private set; }
        public 坐标_ 位置 { get; private set; }
    }

    public class 点及坐标_
    {
        public 点及坐标_(Point 像素点, 坐标_ 坐标)
        {
            this.像素点 = 像素点;
            this.坐标 = 坐标;
        }
        public Point 像素点 { get; private set; }
        public 坐标_ 坐标 { get; private set; }
    }

    public partial class 地图_
    {
        public 地图_(string 储存键, string 名称)
        {
            this.储存键 = 储存键;
            this.名称 = 名称;
            this.属性 |= 地图属性_.未注册地图_;
            this.属性 |= 地图属性_.小型地图_;
            this.分类 = 地图分类_.无_;
            this.X比例 = -1;
            this.Y比例 = -1;
            this.门点组 = new Dictionary<string, 快捷传送_>();
            this.Boss组 = new List<Boss_>();
            this.头目极品组 = new List<怪物_>();
        }
        public 地图_(string 名称) : this(名称, 名称) { }

        public 地图_(string 名称, 地图属性_ 属性, 地图分类_ 分类) : this(名称, 名称, 属性, 分类) { }
        public 地图_(string 储存键, string 名称, 地图属性_ 属性, 地图分类_ 分类) : this(储存键, 名称)
        {
            this.属性 |= 属性;
            this.分类 |= 分类;
            this.属性 |= 地图属性_.小型地图_;
        }

        public 地图_(string 储存键, string 名称, 地图属性_ 属性, 地图分类_ 分类, Point 小地图零点) :
            this(储存键, 名称, 属性, 分类)
        {
            this.小地图零点 = 小地图零点;
            this.门点组 = new Dictionary<string, 快捷传送_>();
        }
        public 地图_(string 名称, 地图属性_ 属性, 地图分类_ 分类, Point 小地图零点) :
            this(名称, 名称, 属性, 分类, 小地图零点)
        { }
        public 地图_(string 名称, 地图属性_ 属性, 地图分类_ 分类, Point 小地图零点, 点及坐标_ 第二点) :
            this(名称, 名称, 属性, 分类, 小地图零点, 第二点)
        { }

        public 地图_(string 储存键, string 名称, 地图属性_ 属性, 地图分类_ 分类, Point 小地图零点, 点及坐标_ 第二点) :
            this(储存键, 名称, 属性, 分类)
        {
            this.小地图零点 = 小地图零点;
            this.门点组 = new Dictionary<string, 快捷传送_>();
            this.第二点 = 第二点;
            if (this.第二点 != null)
            {
                this.X比例 = Math.Round((double)(this.第二点.像素点.X - this.小地图零点.X) / this.第二点.坐标.X, 8);
                this.Y比例 = Math.Round((double)(this.第二点.像素点.Y - this.小地图零点.Y) / this.第二点.坐标.Y, 8);
            }
        }
        public 地图_(string 名称, 地图属性_ 属性, 地图分类_ 分类, Point 小地图零点, Point 第二点像素点, 坐标_ 第二点位置) :
            this(名称, 名称, 属性, 分类, 小地图零点, new 点及坐标_(第二点像素点, 第二点位置))
        { }
        public 地图_(string 储存键, string 名称, 地图属性_ 属性, 地图分类_ 分类, Point 小地图零点, Point 第二点像素点, 坐标_ 第二点位置) :
            this(储存键, 名称, 属性, 分类, 小地图零点, new 点及坐标_(第二点像素点, 第二点位置))
        { }
        public string 储存键 { get; private set; }
        public string 名称 { get; private set; }
        public 地图属性_ 属性 { get; set; }
        private Dictionary<string, 快捷传送_> 门点组 { get; set; }
        public 地图分类_ 分类 { get; private set; }
        private double X比例 { get; set; }
        private double Y比例 { get; set; }
        public Point 小地图零点 { get; set; }
        public 点及坐标_ 第二点 { get; set; }
        public Point 获取对应点(坐标_ 位置)
        {
            var p = new Point(-1, -1);
            if (this.第二点 != null)
            {
                this.获取XY比例(out double X, out double Y);
                p.X = (int)Math.Ceiling(X * 位置.X + this.小地图零点.X);
                p.Y = (int)Math.Ceiling(Y * 位置.Y + this.小地图零点.Y);
            }
            return p;
        }
        public void 获取XY比例(out double X, out double Y)
        {
            X = this.X比例;
            Y = this.Y比例;
        }
        private void 添加点(地图点_ 点)
        {
            if (this.第二点 == null && 点.位置.像素点.X != -1 && 点.位置.像素点.Y != -1 && 点.位置.坐标.X != -1 && 点.位置.坐标.Y != -1)
            {
                this.第二点 = 点.位置;
                this.X比例 = Math.Round((double)(this.第二点.像素点.X - this.小地图零点.X) / this.第二点.坐标.X, 8);
                this.Y比例 = Math.Round((double)(this.第二点.像素点.Y - this.小地图零点.Y) / this.第二点.坐标.Y, 8);
            }
        }
        public void 添加门点(快捷传送_ 门点)
        {
            if (!this.有门点(门点.名称))
            {
                this.添加点(门点);
                this.门点组.Add(门点.名称, 门点);
            }
        }
        public void 添加门点(快捷传送_[] 门点)
        {
            foreach (var v in 门点)
            {
                添加门点(v);
            }
        }
        public void 添加门点(List<快捷传送_> 门点)
        {
            添加门点(门点.ToArray());
        }
        public List<快捷传送_> 获取所有门点()
        {
            return this.门点组.Values.ToList();
        }
        public 快捷传送_ 获取门点(string 名称)
        {
            this.门点组.TryGetValue(名称, out var 值);
            return 值;
        }

        public int 获取随机接受范围值()
        {
            return (this.属性 & 地图属性_.大型地图_) != 0 ? 100 : (this.属性 & 地图属性_.中型地图_) != 0 ? 50 : 10;
        }

        public bool 有门点(string 名称)
        {
            return this.门点组.ContainsKey(名称);
        }
        public 飞书传送_ 飞书传送 { get; set; }
        public List<Boss_> Boss组 { get; set; }
        public List<怪物_> 头目极品组 { get; set; }

        public void 添加Boss_(Boss_ 王)
        {
            this.Boss组.Add(王);
        }

    }

}